#include<stdlib.h>
#include<string.h>
#include<ctype.h>
#include<string>
#include <iterator>
#include<stack>
#include<list>
#include"parser.h"

inline int inList(char c, char *list, int len) {
    for (int i = 0; i < len; ++i) {
        if (list[i] == c) {
            return 1;
        }
    }
    return 0;
}

inline int isString(char c) {
    return c == '"';
}

Parser::Parser() {
    this->split = "\n\r\t ()";
    this->splitLen = strlen(this->split);
    this->regex = "\\(|\\)|#t|#f|[\\+\\-]?\\d+|\\d+\\.\\d+|'|`|\"([^\"]|\\\\|\\\")*\"|[^\\s\\(\\)\"']+";
}

std::list<Token *> *Parser::split(char *code) {
    int len = strlen(code);
    int lineno = 0;
    int offset = 0;
    std::list<Token *> *st = new std::list<Token *>();
    for (int i = 0; i < len; ++i) {
        ++offset;
        if (inList(code[i], this->split, this->splitLen)) {
            if (code[i] == '(') {
                st->push_back(this->makeToken(lineno, offset, Tok_LP, code + i, 1));
            } else if (code[i] == ')') {
                st->push_back(this->makeToken(lineno, offset, Tok_RP, code + i, 1));
            } else if (code[i] == '\n') {
                ++lineno;
                offset = 0;
            }
        } else if (code[i] == '\'') {
            st->push_back(this->makeToken(lineno, offset, Tok_Quote, code + i, 1));
        } else if (code[i] == '"') {
            for (int n = i; n < len; ++n) {
                ++offset;
                if (code[n] == '\\') {
                    continue;
                } else if (code[n] == '"') {
                    st->push_back(this->makeToken(lineno, offset, Tok_String, code + i + 1, n - i - 1));
                    i = n;
                    break;
                } else if (code[n] == '\n') {
                    ++lineno;
                    offset = 0;
                }
            }
        } else {
            for (int n = i; n < len; ++n) {
                ++offset;
                if (inList(code[n], this->split, this->splitLen)) {
                    st->push_back(this->makeToken(lineno, offset, Tok_Symbol, code + i, n - i));
                    i = n - 1;
                    break;
                } else if (code[n] == '\n') {
                    ++lineno;
                    offset = 0;
                }
            }
        }
    }

    return st;
}

Value *Parser::parse(char *code) {
    std::list<Token *> *tokens = this->split(code);
    std::stack<Value *> *st = new std::stack<Value *>();
    Token *token = NULL;
    Value *tmp = NULL;
    while (tokens->size() > 0) {
        token = tokens->pop_front();
        switch (token->type) {
            case Tok_LP:
                tmp = NULL;
                break;
            case Tok_RP:
                std::list<Value *> *fcall = new std::list<Value *>();
                while (1) {
                    tmp = st->top();
                    st->pop();
                    if (tmp == NULL) {
                        tmp = (Value *) malloc(sizeof (Value));
                        tmp->length = fcall->size();
                        tmp->t = T_List;
                        tmp->val.listval = fcall;
                        //st->push(tmp);
                        break;
                    } else {
                        tmp = st->top();
                        st->pop();
                        fcall->push_front(tmp);
                    }
                }
                break;
            case Tok_String:
                tmp = (Value *) malloc(sizeof (Value));
                tmp->t = T_String;
                tmp->val.sval = token->tok->c_str();
                break;
            case Tok_Quote:
                tmp = (Value *) malloc(sizeof (Value));
                tmp->t = T_Quote;
                break;
            case Tok_Symbol:
                char *tok = token->tok->c_str();
                char *err = NULL;
                int intval = strtol(tok, &err, 0);
                if (err == NULL) {
                    tmp = (Value *) malloc(sizeof (Value));
                    tmp->t = T_Integer;
                    tmp->val.ival = intval;
                    
                    break;
                }
                float fval = strtol(tok, &err, 0);
                if (err == NULL) {
                    tmp = (Value *) malloc(sizeof (Value));
                    tmp->t = T_Float;
                    tmp->val.fval = fval;
                    break;
                }
                if (tok[0]=='#'&&tok[1]=='\\') {
                    tmp = (Value *) malloc(sizeof (Value));
                    tmp->t = T_Char;
                    tmp->val.cval = tok[2];
                    break;
                }
                tmp = (Value *) malloc(sizeof (Value));
                tmp->t = T_Symbol;
                tmp->val.symbolval = tok;
                tmp->length = token->tok->length();
                break;
        }
        if (tmp != NULL && st->top()->t == Tok_Quote) {
            std::list<Value *> *fcall = new std::list<Value *>();
            fcall->push_front(tmp);
            tmp = st->top();
            st->pop();
            fcall->push_front(tmp);
            tmp = (Value *) malloc(sizeof (Value));
            tmp->length = fcall->size();
            tmp->t = T_List;
            tmp->val.listval = fcall;
        }
        st->push(tmp);
    }
    return st->top();

}

Token *makeToken(int line, int offset, TokenType type, char *start, int len) {
    Token *ret = (Token *) malloc(sizeof (Token));
    ret->lineno = line;
    ret->offset = offset;
    ret->type = type;
    if (start != NULL) {
        if (len > 0) {
            ret->tok = new std::string(start, len);
        }
        ret->len = len;
    }
    return ret;
}

Parser::~Parser() {

}

